#include "rtp-internal.h"
#include "rtp-util.h"
#include <errno.h>

#define TCC01_CLOCK_RESOLUTION 250 // us

static int rtcp_rtpfb_nack_pack(const rtcp_nack_t *nack, int count, uint8_t *ptr, uint32_t bytes);
static int rtcp_rtpfb_tmmbr_pack(const rtcp_tmmbr_t *tmmbr, int count, uint8_t *ptr, uint32_t bytes);
static int rtcp_rtpfb_tmmbn_pack(const rtcp_tmmbr_t *tmmbr, int count, uint8_t *ptr, uint32_t bytes);
static int rtcp_rtpfb_tllei_pack(const rtcp_nack_t *nack, int count, uint8_t *ptr, uint32_t bytes);
static int rtcp_rtpfb_ecn_pack(const rtcp_ecn_t *ecn, uint8_t *ptr, uint32_t bytes);
static int rtcp_rtpfb_ps_pack(uint32_t target, uint8_t cmd, uint8_t len, uint16_t id, const uint8_t *payload,
                              uint8_t *ptr, uint32_t bytes);
static int rtcp_rtpfb_ccfb_pack(uint32_t ssrc, uint16_t begin, const rtcp_ccfb_t *ccfb, int count, uint32_t timestamp,
                                uint8_t *ptr, uint32_t bytes);
static int rtcp_rtpfb_tcc01_pack(uint16_t begin, const rtcp_ccfb_t *ccfb, int count, uint32_t timestamp, uint8_t cc,
                                 uint8_t *ptr, uint32_t bytes);

static int rtcp_rtpfb_nack_unpack(struct rtp_context *ctx, const rtcp_header_t *header, struct rtcp_msg_t *msg,
                                  const uint8_t *ptr, size_t bytes);
static int rtcp_rtpfb_tmmbr_unpack(struct rtp_context *ctx, const rtcp_header_t *header, struct rtcp_msg_t *msg,
                                   const uint8_t *ptr, size_t bytes);
static int rtcp_rtpfb_tmmbn_unpack(struct rtp_context *ctx, const rtcp_header_t *header, struct rtcp_msg_t *msg,
                                   const uint8_t *ptr, size_t bytes);
static int rtcp_rtpfb_srreq_unpack(struct rtp_context *ctx, const rtcp_header_t *header, struct rtcp_msg_t *msg,
                                   const uint8_t *ptr, size_t bytes);
static int rtcp_rtpfb_rams_unpack(struct rtp_context *ctx, const rtcp_header_t *header, struct rtcp_msg_t *msg,
                                  const uint8_t *ptr, size_t bytes);
static int rtcp_rtpfb_tllei_unpack(struct rtp_context *ctx, const rtcp_header_t *header, struct rtcp_msg_t *msg,
                                   const uint8_t *ptr, size_t bytes);
static int rtcp_rtpfb_ecn_unpack(struct rtp_context *ctx, const rtcp_header_t *header, struct rtcp_msg_t *msg,
                                 const uint8_t *ptr, size_t bytes);
static int rtcp_rtpfb_ps_unpack(struct rtp_context *ctx, const rtcp_header_t *header, struct rtcp_msg_t *msg,
                                const uint8_t *ptr, size_t bytes);
static int rtcp_rtpfb_dbi_unpack(struct rtp_context *ctx, const rtcp_header_t *header, struct rtcp_msg_t *msg,
                                 const uint8_t *ptr, size_t bytes);
static int rtcp_rtpfb_ccfb_unpack(struct rtp_context *ctx, const rtcp_header_t *header, struct rtcp_msg_t *msg,
                                  const uint8_t *ptr, size_t bytes);
static int rtcp_rtpfb_tcc01_unpack(struct rtp_context *ctx, const rtcp_header_t *header, struct rtcp_msg_t *msg,
                                   const uint8_t *ptr, size_t bytes);

// https://datatracker.ietf.org/doc/html/rfc4585#section-6.2.1
/*
    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |            PID                |             BLP               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
static int rtcp_rtpfb_nack_unpack(struct rtp_context *ctx, const rtcp_header_t *header, struct rtcp_msg_t *msg,
                                  const uint8_t *ptr, size_t bytes)
{
    uint32_t i;
    rtcp_nack_t *nack, nack0[32];

    if (bytes / 4 > sizeof(nack0) / sizeof(nack0[0])) {
        nack = calloc(bytes / 4, sizeof(*nack));
        if (!nack)
            return -ENOMEM;
    } else {
        nack = nack0;
        memset(nack, 0, sizeof(nack[0]) * (bytes / 4));
    }

    for (i = 0; i < bytes / 4; i++) {
        nack[i].pid = nbo_r16(ptr);
        nack[i].blp = nbo_r16(ptr + 2);
        ptr += 4;
    }

    msg->u.rtpfb.u.nack.nack = nack;
    msg->u.rtpfb.u.nack.count = i;
    ctx->handler.on_rtcp(ctx->cbparam, msg);
    (void)ctx, (void)header;
    if (nack && nack != nack0)
        free(nack);
    return 0;
}

static int rtcp_rtpfb_nack_pack(const rtcp_nack_t *nack, int count, uint8_t *ptr, uint32_t bytes)
{
    int i;
    for (i = 0; i < count && bytes >= 4; i++) {
        nbo_w16(ptr, nack[i].pid);
        nbo_w16(ptr + 2, nack[i].blp);

        bytes -= 4;
        ptr += 4;
    }
    return i * 4;
}

// https://www.rfc-editor.org/rfc/rfc5104.html#section-4.2.1
/*
    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                              SSRC                             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | MxTBR Exp |  MxTBR Mantissa                 |Measured Overhead|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
static int rtcp_rtpfb_tmmbr_unpack(struct rtp_context *ctx, const rtcp_header_t *header, struct rtcp_msg_t *msg,
                                   const uint8_t *ptr, size_t bytes)
{
    uint32_t i;
    rtcp_tmmbr_t *tmmbr, tmmbr0[4];

    if (bytes / 8 > sizeof(tmmbr0) / sizeof(tmmbr0[0])) {
        tmmbr = calloc(bytes / 8, sizeof(*tmmbr));
        if (!tmmbr)
            return -ENOMEM;
    } else {
        tmmbr = tmmbr0;
        memset(tmmbr, 0, sizeof(tmmbr[0]) * (bytes / 8));
    }

    for (i = 0; i < bytes / 8; i++) {
        tmmbr[i].ssrc = nbo_r32(ptr);
        tmmbr[i].exp = (ptr[4] >> 2) & 0x3F;
        tmmbr[i].mantissa = ((ptr[4] & 0x03) << 15) | (ptr[5] << 7) | ((ptr[6] >> 1) & 0x7F);
        tmmbr[i].overhead = ((ptr[6] & 0x01) << 8) | ptr[7];
        ptr += 8;
    }

    msg->u.rtpfb.u.tmmbr.tmmbr = tmmbr;
    msg->u.rtpfb.u.tmmbr.count = i;
    ctx->handler.on_rtcp(ctx->cbparam, msg);
    (void)ctx, (void)header;
    if (tmmbr && tmmbr != tmmbr0)
        free(tmmbr);
    return 0;
}

static int rtcp_rtpfb_tmmbr_pack(const rtcp_tmmbr_t *tmmbr, int count, uint8_t *ptr, uint32_t bytes)
{
    int i;
    for (i = 0; i < count && bytes >= 8; i++) {
        nbo_w32(ptr, tmmbr[i].ssrc);
        nbo_w32(ptr + 4,
                ((tmmbr[i].exp & 0x3F) << 26) | ((tmmbr[i].mantissa & 0x1FFFF) << 9) | (tmmbr[i].overhead & 0x1FF));

        bytes -= 8;
        ptr += 8;
    }
    return i * 8;
}

// https://www.rfc-editor.org/rfc/rfc5104.html#section-4.2.2
static int rtcp_rtpfb_tmmbn_unpack(struct rtp_context *ctx, const rtcp_header_t *header, struct rtcp_msg_t *msg,
                                   const uint8_t *ptr, size_t bytes)
{
    uint32_t i;
    rtcp_tmmbr_t *tmmbr, tmmbr0[4];

    if (bytes / 8 > sizeof(tmmbr0) / sizeof(tmmbr0[0])) {
        tmmbr = calloc(bytes / 8, sizeof(*tmmbr));
        if (!tmmbr)
            return -ENOMEM;
    } else {
        tmmbr = tmmbr0;
        memset(tmmbr, 0, sizeof(tmmbr[0]) * (bytes / 8));
    }

    for (i = 0; i < bytes / 8; i++) {
        tmmbr[i].ssrc = nbo_r32(ptr);
        tmmbr[i].exp = (ptr[4] >> 2) & 0x3F;
        tmmbr[i].mantissa = ((ptr[4] & 0x03) << 15) | (ptr[5] << 7) | ((ptr[6] >> 1) & 0x7F);
        tmmbr[i].overhead = ((ptr[6] & 0x01) << 8) | ptr[7];
        ptr += 8;
    }

    msg->u.rtpfb.u.tmmbr.tmmbr = tmmbr;
    msg->u.rtpfb.u.tmmbr.count = i;
    ctx->handler.on_rtcp(ctx->cbparam, msg);
    (void)ctx, (void)header;
    if (tmmbr && tmmbr != tmmbr0)
        free(tmmbr);
    return 0;
}

static int rtcp_rtpfb_tmmbn_pack(const rtcp_tmmbr_t *tmmbr, int count, uint8_t *ptr, uint32_t bytes)
{
    return rtcp_rtpfb_tmmbr_pack(tmmbr, count, ptr, bytes);
}

// https://www.rfc-editor.org/rfc/rfc6051.html#section-3.2
static int rtcp_rtpfb_srreq_unpack(struct rtp_context *ctx, const rtcp_header_t *header, struct rtcp_msg_t *msg,
                                   const uint8_t *ptr, size_t bytes)
{
    // The SSRC of the packet sender indicates the member that is unable to synchronise media streams,
    // while the SSRC of the media source indicates the sender of the media it is unable to synchronise.
    // The length MUST equal 2.
    assert(bytes == 0);
    (void)ctx, (void)header, (void)msg, (void)ptr;
    return 0;
}

// https://www.rfc-editor.org/rfc/rfc6285.html#section-7
/*
      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |     Type      |   Reserved    |            Length             |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     :                             Value                             :
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

                   Figure 5: Structure of a TLV Element

      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |    SFMT=1     |                    Reserved                   |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     :                  Requested Media Sender SSRC(s)               :
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     :      Optional TLV-encoded Fields (and Padding, if needed)     :
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

          Figure 7: FCI Field Syntax for the RAMS Request Message

      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |    SFMT=2     |      MSN      |          Response             |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     :      Optional TLV-encoded Fields (and Padding, if needed)     :
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

        Figure 8: FCI Field Syntax for the RAMS Information Message
*/
static int rtcp_rtpfb_rams_unpack(struct rtp_context *ctx, const rtcp_header_t *header, struct rtcp_msg_t *msg,
                                  const uint8_t *ptr, size_t bytes)
{
    uint8_t sfmt;
    if (bytes < 4)
        return -1;
    sfmt = ptr[0];
    (void)ctx, (void)header, (void)msg;
    return 0;
}

// https://www.rfc-editor.org/rfc/rfc6642.html#section-5.1
/*
       0                   1                   2                   3
       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |            PID                |             BLP               |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
static int rtcp_rtpfb_tllei_unpack(struct rtp_context *ctx, const rtcp_header_t *header, struct rtcp_msg_t *msg,
                                   const uint8_t *ptr, size_t bytes)
{
    uint32_t i;
    rtcp_nack_t *nack, nack0[32];

    if (bytes / 4 > sizeof(nack0) / sizeof(nack0[0])) {
        nack = calloc(bytes / 4, sizeof(*nack));
        if (!nack)
            return -ENOMEM;
    } else {
        nack = nack0;
        memset(nack, 0, sizeof(nack[0]) * (bytes / 4));
    }

    for (i = 0; i < bytes / 4; i++) {
        nack[i].pid = nbo_r16(ptr);
        nack[i].blp = nbo_r16(ptr + 2);
        ptr += 4;
    }

    msg->u.rtpfb.u.nack.nack = nack;
    msg->u.rtpfb.u.nack.count = i;
    ctx->handler.on_rtcp(ctx->cbparam, msg);
    (void)ctx, (void)header;
    if (nack && nack != nack0)
        free(nack);
    return 0;
}

static int rtcp_rtpfb_tllei_pack(const rtcp_nack_t *nack, int count, uint8_t *ptr, uint32_t bytes)
{
    return rtcp_rtpfb_nack_pack(nack, count, ptr, bytes);
}

// https://www.rfc-editor.org/rfc/rfc6679.html#section-5.1
/*
    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | Extended Highest Sequence Number                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | ECT (0) Counter                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | ECT (1) Counter                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | ECN-CE Counter                | not-ECT Counter               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | Lost Packets Counter          | Duplication Counter           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
static int rtcp_rtpfb_ecn_unpack(struct rtp_context *ctx, const rtcp_header_t *header, struct rtcp_msg_t *msg,
                                 const uint8_t *ptr, size_t bytes)
{
    rtcp_ecn_t ecn;
    if (bytes < 20)
        return -1;
    ecn.ext_highest_seq = nbo_r32(ptr);
    ecn.ect[0] = nbo_r32(ptr + 4);
    ecn.ect[1] = nbo_r32(ptr + 8);
    ecn.ect_ce_counter = nbo_r16(ptr + 12);
    ecn.not_ect_counter = nbo_r16(ptr + 14);
    ecn.lost_packets_counter = nbo_r16(ptr + 16);
    ecn.duplication_counter = nbo_r16(ptr + 18);

    memcpy(&msg->u.rtpfb.u.ecn, &ecn, sizeof(ecn));
    ctx->handler.on_rtcp(ctx->cbparam, msg);
    (void)ctx, (void)header;
    return 0;
}

static int rtcp_rtpfb_ecn_pack(const rtcp_ecn_t *ecn, uint8_t *ptr, uint32_t bytes)
{
    if (bytes < 20)
        return -1;

    nbo_w32(ptr, ecn->ext_highest_seq);
    nbo_w32(ptr + 4, ecn->ect[0]);
    nbo_w32(ptr + 8, ecn->ect[1]);
    nbo_w16(ptr + 12, ecn->ect_ce_counter);
    nbo_w16(ptr + 14, ecn->not_ect_counter);
    nbo_w16(ptr + 16, ecn->lost_packets_counter);
    nbo_w16(ptr + 18, ecn->duplication_counter);
    return 20;
}

// https://www.rfc-editor.org/rfc/rfc7728.html#section-7
/*
      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                           Target SSRC                         |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     | Type  |  Res  | Parameter Len |           PauseID             |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     :                         Type Specific                         :
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
static int rtcp_rtpfb_ps_unpack(struct rtp_context *ctx, const rtcp_header_t *header, struct rtcp_msg_t *msg,
                                const uint8_t *ptr, size_t bytes)
{
    uint16_t id;
    uint32_t target;
    uint8_t cmd;
    uint32_t len;

    while (bytes >= 8) {
        target = nbo_r32(ptr); // target ssrc
        cmd = ptr[4] >> 4;
        len = ptr[5];
        id = nbo_r16(ptr + 6);

        if (len * 4 + 8 > bytes) {
            assert(0);
            return -1;
        }

        msg->u.rtpfb.u.ps.target = target;
        msg->u.rtpfb.u.ps.cmd = cmd;
        msg->u.rtpfb.u.ps.len = len;
        msg->u.rtpfb.u.ps.id = id;
        msg->u.rtpfb.u.ps.payload = (uint8_t *)ptr + 8;
        ctx->handler.on_rtcp(ctx->cbparam, msg);

        ptr += 8 + len * 4;
        bytes -= 8 + len * 4;
    }

    (void)ctx, (void)header;
    return 0;
}

static int rtcp_rtpfb_ps_pack(uint32_t target, uint8_t cmd, uint8_t len, uint16_t id, const uint8_t *payload,
                              uint8_t *ptr, uint32_t bytes)
{
    if (bytes < 8 + (uint32_t)len * 4)
        return -1;

    nbo_w32(ptr, target);
    nbo_w32(ptr + 4, ((cmd & 0x0F) << 28) | ((len & 0xFF) << 16) | id);
    if (len > 0)
        memcpy(ptr + 8, payload, len * 4);
    return 8 + len * 4;
}

// https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=1404
/*
 0                1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|            delay              |s|q|     zero padding          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
static int rtcp_rtpfb_dbi_unpack(struct rtp_context *ctx, const rtcp_header_t *header, struct rtcp_msg_t *msg,
                                 const uint8_t *ptr, size_t bytes)
{
    if (bytes < 4)
        return -1;

    msg->u.rtpfb.u.dbi.delay = nbo_r16(ptr);
    msg->u.rtpfb.u.dbi.s = (ptr[2] >> 7) & 0x01;
    msg->u.rtpfb.u.dbi.q = (ptr[2] >> 6) & 0x01;
    ctx->handler.on_rtcp(ctx->cbparam, msg);
    (void)ctx, (void)header, (void)ptr;
    return 0;
}

// https://www.rfc-editor.org/rfc/rfc8888.html#section-3.1
/*
   0                   1                   2                   3
   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |V=2|P| FMT=11  |   PT = 205    |          length               |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                 SSRC of RTCP packet sender                    |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                   SSRC of 1st RTP Stream                      |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |          begin_seq            |          num_reports          |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |R|ECN|  Arrival time offset    | ...                           .
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  .                                                               .
  .                                                               .
  .                                                               .
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                   SSRC of nth RTP Stream                      |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |          begin_seq            |          num_reports          |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |R|ECN|  Arrival time offset    | ...                           |
  .                                                               .
  .                                                               .
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                 Report Timestamp (32 bits)                    |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
static int rtcp_rtpfb_ccfb_unpack(struct rtp_context *ctx, const rtcp_header_t *header, struct rtcp_msg_t *msg,
                                  const uint8_t *ptr, size_t bytes)
{
    uint32_t i, num, ssrc;
    uint32_t timestamp;
    uint16_t begin;
    rtcp_ccfb_t *ccfb, ccfb0[32];

    timestamp = bytes >= 4 ? nbo_r32(ptr + bytes - 4) : 0; // get last report timestamp

    while (bytes >= 8) {
        ssrc = nbo_r32(ptr); // target ssrc
        begin = nbo_r16(ptr + 4);
        num = nbo_r16(ptr + 6);

        ptr += 8;
        bytes -= 8;
        if ((num * 2 + 3) / 4 * 4 > bytes) // 4-bytes padding
        {
            assert(0);
            return -1;
        }

        if (num > sizeof(ccfb0) / sizeof(ccfb0[0])) {
            ccfb = calloc(num, sizeof(*ccfb));
            if (!ccfb)
                return -ENOMEM;
        } else {
            ccfb = ccfb0;
            memset(ccfb, 0, sizeof(ccfb[0]) * num);
        }

        for (i = 0; i < num; i++) {
            ccfb[i].seq = begin + i;
            ccfb[i].received = (ptr[i * 2] >> 7) & 0x01;
            ccfb[i].ecn = (ptr[i * 2] >> 5) & 0x03;
            ccfb[i].ato = ((ptr[i * 2] & 0x1F) << 8) | ptr[i * 2 + 1];
        }

        msg->u.rtpfb.u.tcc01.timestamp = 0; // fixme
        msg->u.rtpfb.u.tcc01.ssrc = ssrc;
        msg->u.rtpfb.u.tcc01.begin = begin;
        msg->u.rtpfb.u.tcc01.ccfb = ccfb;
        msg->u.rtpfb.u.tcc01.count = i;
        ctx->handler.on_rtcp(ctx->cbparam, msg);

        num = (num + 1) / 2 * 2;  // 4-bytes padding
        assert(bytes >= num * 2); // check again
        ptr += num * 2;
        bytes -= num * 2;

        if (ccfb && ccfb != ccfb0)
            free(ccfb);
    }

    if (bytes >= 4) {
        timestamp = nbo_r32(ptr);
        ptr += 4;
        bytes -= 4;
    }

    (void)ctx, (void)header;
    assert(0 == bytes);
    return 0;
}

static int rtcp_rtpfb_ccfb_pack(uint32_t ssrc, uint16_t begin, const rtcp_ccfb_t *ccfb, int count, uint32_t timestamp,
                                uint8_t *ptr, uint32_t bytes)
{
    int i;
    if (bytes < 8 + (uint32_t)count * 2 + 4 || count > 0xFFFF)
        return -1;

    nbo_w32(ptr, ssrc);
    nbo_w16(ptr + 4, begin);
    nbo_w16(ptr + 6, (uint16_t)count);
    ptr += 8;

    for (i = 0; i < count; i++) {
        nbo_w16(ptr, (ccfb[i].received ? 0x8000 : 0) | ((ccfb[i].ecn & 0x03) << 13) | (ccfb->ato & 0x1FFF));
        bytes -= 2;
        ptr += 2;
    }

    nbo_w32(ptr, timestamp);
    return 8 + count * 2 + 4;
}

// https://datatracker.ietf.org/doc/html/draft-holmer-rmcat-transport-wide-cc-extensions-00#section-3.1
/*
     0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     | fb seq num                  |r|       base sequence number    |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |       base receive time       |  sequence number ack vector   |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     | recv delta        | recv delta        | recv delta        |...|
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     .                                                               .
     .                                                               .
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     | recovery base sequence number |  recovery vector              |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/

// https://datatracker.ietf.org/doc/html/draft-holmer-rmcat-transport-wide-cc-extensions#section-3.1
/*
        0                   1                   2                   3
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |V=2|P|  FMT=15 |    PT=205     |           length              |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                     SSRC of packet sender                     |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                      SSRC of media source                     |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |      base sequence number     |      packet status count      |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                 reference time                | fb pkt. count |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |          packet chunk         |         packet chunk          |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       .                                                               .
       .                                                               .
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |         packet chunk          |  recv delta   |  recv delta   |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       .                                                               .
       .                                                               .
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |           recv delta          |  recv delta   | zero padding  |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
static int rtcp_rtpfb_tcc01_unpack(struct rtp_context *ctx, const rtcp_header_t *header, struct rtcp_msg_t *msg,
                                   const uint8_t *ptr, size_t bytes)
{
    int r;
    uint16_t i, j;
    uint32_t timestamp;
    uint16_t seq, num, chunk;
    uint8_t cc;
    rtcp_ccfb_t *ccfb, ccfb0[32];

    if (bytes < 8)
        return -1;
    seq = nbo_r16(ptr);
    num = nbo_r16(ptr + 2);
    timestamp = (ptr[4] << 16) | (ptr[5] << 8) | ptr[6];
    cc = ptr[7];
    msg->u.rtpfb.u.tcc01.cc = cc;
    msg->u.rtpfb.u.tcc01.begin = seq;
    msg->u.rtpfb.u.tcc01.timestamp = timestamp | ((timestamp & 0x00800000) ? 0xFF000000 : 0); // signed-24 -> signed-32

    if (num > sizeof(ccfb0) / sizeof(ccfb0[0])) {
        ccfb = calloc(num, sizeof(*ccfb));
        if (!ccfb)
            return -ENOMEM;
    } else {
        ccfb = ccfb0;
        memset(ccfb, 0, sizeof(ccfb[0]) * num);
    }

    r = 0;
    ptr += 8;
    bytes -= 8;
    for (i = 0; bytes >= 2 && i < num; bytes -= 2, ptr += 2) {
        chunk = nbo_r16(ptr); // packet chunk

        if (0 == (0x8000 & chunk)) {
            // Run Length Chunk
            for (j = 0; j < (uint16_t)(chunk & 0x1FFF) && i < num; j++, i++) {
                ccfb[i].seq = seq++;
                ccfb[i].ecn = (chunk >> 13) & 0x03;
                ccfb[i].received = ccfb[i].ecn ? 1 : 0;
                ccfb[i].ato = 0;
            }
        } else {
            // Status Vector Chunk
            if (0x4000 & chunk) {
                // two bits
                for (j = 0; j < 7 && i < num; j++, i++) {
                    ccfb[i].seq = seq++;
                    ccfb[i].ecn = (chunk >> (2 * (6 - j))) & 0x03;
                    ccfb[i].received = ccfb[i].ecn ? 1 : 0;
                    ccfb[i].ato = 0;
                }
            } else {
                // one bits
                for (j = 0; j < 14 && i < num; j++, i++) {
                    ccfb[i].seq = seq++;
                    ccfb[i].ecn = (chunk & (1 << (13 - j))) ? 0x01 : 0x00; // small delta
                    ccfb[i].received = ccfb[i].ecn ? 1 : 0;
                    ccfb[i].ato = 0;
                }
            }
        }
    }

    for (i = 0; i < num && bytes > 0; i++) {
        if (!ccfb[i].received)
            continue;

        assert(ccfb[i].ecn == 0x01 || ccfb[i].ecn == 0x02);
        if (ccfb[i].ecn == 0x01) {
            ccfb[i].ato = ptr[0] >> 2; // 250us
            bytes -= 1;
            ptr += 1;
        } else {
            if (bytes < 2) {
                assert(0);
                r = -1;
                break;
            }
            ccfb[i].ato = ((int16_t)nbo_r16(ptr)) >> 2; // 250us -> 1/1024(ms)
            bytes -= 2;
            ptr += 2;
        }
    }

    if (0 == r) {
        msg->u.rtpfb.u.tcc01.ccfb = ccfb;
        msg->u.rtpfb.u.tcc01.count = num;
        ctx->handler.on_rtcp(ctx->cbparam, msg);
    }
    (void)ctx, (void)header;
    if (ccfb && ccfb != ccfb0)
        free(ccfb);
    return r;
}

static int rtcp_rtpfb_tcc01_pack(uint16_t begin, const rtcp_ccfb_t *ccfb, int count, uint32_t timestamp, uint8_t cc,
                                 uint8_t *ptr, uint32_t bytes)
{
    int i, k, n;
    int16_t ato;
    uint16_t chunk, two;
    uint32_t seq, received;
    const uint8_t *p;
    if (bytes < 8 || count < 1)
        return -1;

    nbo_w16(ptr, begin);
    nbo_w16(ptr + 2, (uint16_t)count); // placeholder
    nbo_w32(ptr + 4, ((timestamp & 0xFFFFFF) << 8) | cc);
    ptr += 8;
    p = ptr; // chunk pointer
    n = 8;

    for (i = 0; i < count && bytes >= 2; i += k) {
        ato = ccfb[i].ato << 2; // 1/1024(ms) -> 250us
        two = (ccfb[i].received && (uint16_t)ato > 0xFF) ? 2 : 1;
        received = ccfb[i].received;

        // try Run Length Chunk
        for (k = 1; i + k < count && ccfb[i + k].received == ccfb[i].received; k++) {
            ato = ccfb[i + k].ato << 2; // 1/1024(ms) -> 250us
            two = (ccfb[i + k].received && (uint16_t)ato > 0xFF) ? 2 : two;
            received |= ccfb[i + k].received;
        }

        if (k > 14 / two) {
            // Run Length Chunk
            chunk = 0x0000 | (received ? (2 == two ? 0x4000 : 0x2000) : 0x0000) | (uint16_t)k;
        } else {
            two = 1; // re-detect
            for (k = 0; k < 14 / two && i + k < count; k++) {
                ato = ccfb[i + k].ato << 2; // 1/1024(s) -> 250us
                two = (ccfb[i + k].received && (uint16_t)ato > 0xFF) ? 2 : two;
            }

            ato = (2 == two || k <= 7) ? 1 : 0; // small space/padding
            chunk = 0x8000 | (ato ? 0x4000 : 0x0000);
            for (k = 0; k < 14 / two && i + k < count; k++) {
                if (ccfb[i + k].received) {
                    chunk |= ((uint16_t)(ccfb[i + k].ato << 2) > 0xFF ? 2 : 1) << (ato ? (12 - k * 2) : (13 - k));
                }
            }
        }

        nbo_w16(ptr, chunk);
        bytes -= 2;
        ptr += 2;
        n += 2;
    }

    // parse chunk and write delta
    for (i = 0; i < count && bytes >= 1; p += 2) {
        chunk = nbo_r16(p); // packet chunk

        if (0 == (0x8000 & chunk)) {
            // Run Length Chunk
            seq = ccfb[i].seq;
            for (k = 0; k < (uint16_t)(chunk & 0x1FFF) && i < count; k++) {
                assert(seq + k == ccfb[i].seq);
                if (seq + k != ccfb[i].seq)
                    continue;

                ato = ccfb[i].ato << 2; // 1/1024(ms) -> 250us
                if (chunk & 0x4000) {
                    if (bytes < 2)
                        return -1;
                    nbo_w16(ptr, ato);
                    bytes -= 2;
                    ptr += 2;
                    n += 2;
                } else if (chunk & 0x2000) {
                    if (bytes < 1)
                        return -1;
                    ptr[0] = (uint8_t)ato;
                    bytes -= 1;
                    ptr += 1;
                    n += 1;
                }

                i++;
            }
        } else {
            // Status Vector Chunk
            if (0x4000 & chunk) {
                // two bits
                for (k = 0; k < 7 && i < count && bytes >= 2; k++, i++) {
                    if (!ccfb[i].received)
                        continue;

                    ato = ccfb[i].ato << 2; // 1/1024(ms) -> 250us
                    two = (chunk >> (2 * (6 - k))) & 0x03;
                    if (two == 1) {
                        ptr[0] = (uint8_t)ato;
                        bytes -= 1;
                        ptr += 1;
                        n += 1;
                    } else {
                        nbo_w16(ptr, ato);
                        bytes -= 2;
                        ptr += 2;
                        n += 2;
                    }
                }
            } else {
                // one bits
                for (k = 0; k < 14 && i < count && bytes >= 1; k++, i++) {
                    if (!ccfb[i].received)
                        continue;

                    ato = ccfb[i].ato << 2; // 1/1024(ms) -> 250us
                    ptr[0] = (uint8_t)ato;
                    bytes -= 1;
                    ptr += 1;
                    n += 1;
                }
            }
        }
    }

    // padding
    for (k = 0; k < 4 && (n % 4 != 0) && bytes > 0; k++) {
        ptr[0] = ((n + 1) % 4 == 0) ? (uint8_t)k + 1 : 0;
        bytes -= 1;
        ptr += 1;
        n += 1;
    }
    return n;
}

// https://datatracker.ietf.org/doc/html/rfc4585#section-6.2
/*
*  Common Packet Format for Feedback Messages

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |V=2|P|   FMT   |       PT      |          length               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                  SSRC of packet sender                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                  SSRC of media source                         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   :            Feedback Control Information (FCI)                 :
   :                                                               :
*/
void rtcp_rtpfb_unpack(struct rtp_context *ctx, const rtcp_header_t *header, const uint8_t *ptr, size_t bytes)
{
    int r;
    struct rtcp_msg_t msg;
    struct rtp_member *sender;

    if (bytes < 8 /*sizeof(rtcp_fci_t)*/) {
        assert(0);
        return;
    }

    msg.type = RTCP_RTPFB | (header->rc << 8);
    msg.ssrc = nbo_r32(ptr);
    msg.u.rtpfb.media = nbo_r32(ptr + 4);

    sender = rtp_sender_fetch(ctx, msg.ssrc);
    if (!sender)
        return; // error
    // assert(sender != ctx->self);

    switch (header->rc) {
    case RTCP_RTPFB_NACK:
        r = rtcp_rtpfb_nack_unpack(ctx, header, &msg, ptr + 8, bytes - 8);
        break;

    case RTCP_RTPFB_TMMBR:
        r = rtcp_rtpfb_tmmbr_unpack(ctx, header, &msg, ptr + 8, bytes - 8);
        break;

    case RTCP_RTPFB_TMMBN:
        r = rtcp_rtpfb_tmmbn_unpack(ctx, header, &msg, ptr + 8, bytes - 8);
        break;

    case RTCP_RTPFB_SRREQ:
        r = rtcp_rtpfb_srreq_unpack(ctx, header, &msg, ptr + 8, bytes - 8);
        break;

    case RTCP_RTPFB_RAMS:
        r = rtcp_rtpfb_rams_unpack(ctx, header, &msg, ptr + 8, bytes - 8);
        break;

    case RTCP_RTPFB_TLLEI:
        r = rtcp_rtpfb_tllei_unpack(ctx, header, &msg, ptr + 8, bytes - 8);
        break;

    case RTCP_RTPFB_ECN:
        r = rtcp_rtpfb_ecn_unpack(ctx, header, &msg, ptr + 8, bytes - 8);
        break;

    case RTCP_RTPFB_PS:
        r = rtcp_rtpfb_ps_unpack(ctx, header, &msg, ptr + 8, bytes - 8);
        break;

    case RTCP_RTPFB_DBI:
        r = rtcp_rtpfb_dbi_unpack(ctx, header, &msg, ptr + 8, bytes - 8);
        break;

    case RTCP_RTPFB_CCFB:
        r = rtcp_rtpfb_ccfb_unpack(ctx, header, &msg, ptr + 4 /*1st ssrc*/, bytes - 4);
        break;

    case RTCP_RTPFB_TCC01:
        r = rtcp_rtpfb_tcc01_unpack(ctx, header, &msg, ptr + 8, bytes - 8);
        break;

    default:
        assert(0);
        r = 0;
        break;
    }

    return;
}

int rtcp_rtpfb_pack(struct rtp_context *ctx, uint8_t *data, int bytes, enum rtcp_rtpfb_type_t id,
                    const rtcp_rtpfb_t *rtpfb)
{
    int r;
    rtcp_header_t header;

    (void)ctx;
    if (bytes < 4 + 4 + 4)
        return 4 + 4 + 4;

    switch (id) {
    case RTCP_RTPFB_NACK:
        r = rtcp_rtpfb_nack_pack(rtpfb->u.nack.nack, rtpfb->u.nack.count, data + 12, bytes - 12);
        break;

    case RTCP_RTPFB_TMMBR:
        r = rtcp_rtpfb_tmmbr_pack(rtpfb->u.tmmbr.tmmbr, rtpfb->u.tmmbr.count, data + 12, bytes - 12);
        break;

    case RTCP_RTPFB_TMMBN:
        r = rtcp_rtpfb_tmmbn_pack(rtpfb->u.tmmbr.tmmbr, rtpfb->u.tmmbr.count, data + 12, bytes - 12);
        break;

    case RTCP_RTPFB_TLLEI:
        r = rtcp_rtpfb_tllei_pack(rtpfb->u.nack.nack, rtpfb->u.nack.count, data + 12, bytes - 12);
        break;

    case RTCP_RTPFB_ECN:
        r = rtcp_rtpfb_ecn_pack(&rtpfb->u.ecn, data + 12, bytes - 12);
        break;

    case RTCP_RTPFB_PS:
        r = rtcp_rtpfb_ps_pack(rtpfb->u.ps.target, (uint8_t)rtpfb->u.ps.cmd, (uint8_t)rtpfb->u.ps.len,
                               (uint16_t)rtpfb->u.ps.id, (const uint8_t *)rtpfb->u.ps.payload, data + 12, bytes - 12);
        break;

    case RTCP_RTPFB_CCFB:
        r = rtcp_rtpfb_ccfb_pack(rtpfb->u.tcc01.ssrc, (uint16_t)rtpfb->u.tcc01.begin, rtpfb->u.tcc01.ccfb,
                                 rtpfb->u.tcc01.count, rtpfb->u.tcc01.timestamp, data + 12, bytes - 12);
        break;

    case RTCP_RTPFB_TCC01:
        r = rtcp_rtpfb_tcc01_pack((uint16_t)rtpfb->u.tcc01.begin, rtpfb->u.tcc01.ccfb, rtpfb->u.tcc01.count,
                                  rtpfb->u.tcc01.timestamp, (uint8_t)rtpfb->u.tcc01.cc, data + 12, bytes - 12);
        break;

    case RTCP_RTPFB_SRREQ:
    case RTCP_RTPFB_RAMS:
    case RTCP_RTPFB_DBI:
    default:
        assert(0);
        return -1;
    }

    header.v = 2;
    header.p = 0;
    header.pt = RTCP_RTPFB;
    header.rc = id;
    header.length = (r + 8 + 3) / 4;
    nbo_write_rtcp_header(data, &header);

    nbo_w32(data + 4, ctx->self->ssrc);
    // nbo_w32(data + 4, rtpfb->sender);
    nbo_w32(data + 8, rtpfb->media);

    // assert(8 == (header.length + 1) * 4);
    return header.length * 4 + 4;
}

#if defined(_DEBUG) || defined(DEBUG)
static void rtcp_on_rtpfb_test(void *param, const struct rtcp_msg_t *msg)
{
    int r;
    static uint8_t buffer[1400];
    switch (msg->type & 0xFF) {
    case RTCP_RTPFB:
        switch ((msg->type >> 8) & 0xFF) {
        case RTCP_RTPFB_NACK:
            assert(13 == msg->u.rtpfb.u.nack.count);
            assert(msg->u.rtpfb.u.nack.nack[0].pid == 631 && msg->u.rtpfb.u.nack.nack[0].blp == 0x8028);
            assert(msg->u.rtpfb.u.nack.nack[1].pid == 648 && msg->u.rtpfb.u.nack.nack[1].blp == 0x0021);
            assert(msg->u.rtpfb.u.nack.nack[2].pid == 666 && msg->u.rtpfb.u.nack.nack[2].blp == 0x0008);
            assert(msg->u.rtpfb.u.nack.nack[3].pid == 690 && msg->u.rtpfb.u.nack.nack[3].blp == 0x2000);
            assert(msg->u.rtpfb.u.nack.nack[4].pid == 734 && msg->u.rtpfb.u.nack.nack[4].blp == 0x0025);
            assert(msg->u.rtpfb.u.nack.nack[5].pid == 757 && msg->u.rtpfb.u.nack.nack[5].blp == 0x1100);
            assert(msg->u.rtpfb.u.nack.nack[6].pid == 777 && msg->u.rtpfb.u.nack.nack[6].blp == 0x0000);
            assert(msg->u.rtpfb.u.nack.nack[7].pid == 826 && msg->u.rtpfb.u.nack.nack[7].blp == 0x0002);
            assert(msg->u.rtpfb.u.nack.nack[8].pid == 865 && msg->u.rtpfb.u.nack.nack[8].blp == 0x0000);
            assert(msg->u.rtpfb.u.nack.nack[9].pid == 882 && msg->u.rtpfb.u.nack.nack[9].blp == 0x0300);
            assert(msg->u.rtpfb.u.nack.nack[10].pid == 907 && msg->u.rtpfb.u.nack.nack[10].blp == 0x0400);
            assert(msg->u.rtpfb.u.nack.nack[11].pid == 931 && msg->u.rtpfb.u.nack.nack[11].blp == 0x0000);
            assert(msg->u.rtpfb.u.nack.nack[12].pid == 963 && msg->u.rtpfb.u.nack.nack[12].blp == 0x006d);
            r = rtcp_rtpfb_nack_pack(msg->u.rtpfb.u.nack.nack, msg->u.rtpfb.u.nack.count, buffer, sizeof(buffer));
            assert(r > 0 && 0 == memcmp(buffer, param, r));
            break;

        case RTCP_RTPFB_TMMBR:
            assert(1 == msg->u.rtpfb.u.tmmbr.count);
            assert(0x23456789 == msg->u.rtpfb.u.tmmbr.tmmbr[0].ssrc && 2 == msg->u.rtpfb.u.tmmbr.tmmbr[0].exp &&
                   78000 == msg->u.rtpfb.u.tmmbr.tmmbr[0].mantissa &&
                   312000 == (msg->u.rtpfb.u.tmmbr.tmmbr[0].mantissa << msg->u.rtpfb.u.tmmbr.tmmbr[0].exp) &&
                   0x1fe == msg->u.rtpfb.u.tmmbr.tmmbr[0].overhead);
            r = rtcp_rtpfb_tmmbr_pack(msg->u.rtpfb.u.tmmbr.tmmbr, msg->u.rtpfb.u.tmmbr.count, buffer, sizeof(buffer));
            assert(8 == r && 0 == memcmp(buffer, param, r));
            break;

        case RTCP_RTPFB_TMMBN:
            assert(1 == msg->u.rtpfb.u.tmmbr.count);
            assert(0x23456789 == msg->u.rtpfb.u.tmmbr.tmmbr[0].ssrc && 2 == msg->u.rtpfb.u.tmmbr.tmmbr[0].exp &&
                   78000 == msg->u.rtpfb.u.tmmbr.tmmbr[0].mantissa &&
                   312000 == (msg->u.rtpfb.u.tmmbr.tmmbr[0].mantissa << msg->u.rtpfb.u.tmmbr.tmmbr[0].exp) &&
                   0x1fe == msg->u.rtpfb.u.tmmbr.tmmbr[0].overhead);
            r = rtcp_rtpfb_tmmbr_pack(msg->u.rtpfb.u.tmmbr.tmmbr, msg->u.rtpfb.u.tmmbr.count, buffer, sizeof(buffer));
            assert(8 == r && 0 == memcmp(buffer, param, r));
            break;

        case RTCP_RTPFB_TCC01:
            assert(1767 == msg->u.rtpfb.u.tcc01.begin && 4114000 == msg->u.rtpfb.u.tcc01.timestamp &&
                   101 == msg->u.rtpfb.u.tcc01.count && 42 == msg->u.rtpfb.u.tcc01.cc);
            assert(msg->u.rtpfb.u.tcc01.ccfb[0].seq == 1767 && msg->u.rtpfb.u.tcc01.ccfb[0].received == 0);
            assert(msg->u.rtpfb.u.tcc01.ccfb[1].seq == 1768 && msg->u.rtpfb.u.tcc01.ccfb[1].received == 1 &&
                   msg->u.rtpfb.u.tcc01.ccfb[1].ato == 21);
            assert(msg->u.rtpfb.u.tcc01.ccfb[2].seq == 1769 && msg->u.rtpfb.u.tcc01.ccfb[2].received == 1 &&
                   msg->u.rtpfb.u.tcc01.ccfb[2].ato == 0);
            assert(msg->u.rtpfb.u.tcc01.ccfb[3].seq == 1770 && msg->u.rtpfb.u.tcc01.ccfb[3].received == 0);
            assert(msg->u.rtpfb.u.tcc01.ccfb[4].seq == 1771 && msg->u.rtpfb.u.tcc01.ccfb[4].received == 1 &&
                   msg->u.rtpfb.u.tcc01.ccfb[4].ato == 0);
            assert(msg->u.rtpfb.u.tcc01.ccfb[5].seq == 1772 && msg->u.rtpfb.u.tcc01.ccfb[5].received == 0);
            assert(msg->u.rtpfb.u.tcc01.ccfb[6].seq == 1773 && msg->u.rtpfb.u.tcc01.ccfb[6].received == 1 &&
                   msg->u.rtpfb.u.tcc01.ccfb[6].ato == 26);
            r = rtcp_rtpfb_tcc01_pack((uint16_t)msg->u.rtpfb.u.tcc01.begin, msg->u.rtpfb.u.tcc01.ccfb,
                                      msg->u.rtpfb.u.tcc01.count, msg->u.rtpfb.u.tcc01.timestamp,
                                      (uint8_t)msg->u.rtpfb.u.tcc01.cc, buffer, sizeof(buffer));
            assert(104 == r && 0 == memcmp(buffer, param, r));
            break;

        case 30: // test only
            assert(5 == msg->u.rtpfb.u.tcc01.begin && 5 == msg->u.rtpfb.u.tcc01.timestamp &&
                   7 == msg->u.rtpfb.u.tcc01.count && 0 == msg->u.rtpfb.u.tcc01.cc);
            assert(msg->u.rtpfb.u.tcc01.ccfb[0].seq == 5 && msg->u.rtpfb.u.tcc01.ccfb[0].received == 1 &&
                   msg->u.rtpfb.u.tcc01.ccfb[1].ato == 0);
            assert(msg->u.rtpfb.u.tcc01.ccfb[1].seq == 6 && msg->u.rtpfb.u.tcc01.ccfb[1].received == 0);
            assert(msg->u.rtpfb.u.tcc01.ccfb[2].seq == 7 && msg->u.rtpfb.u.tcc01.ccfb[2].received == 1 &&
                   msg->u.rtpfb.u.tcc01.ccfb[2].ato == 128);
            assert(msg->u.rtpfb.u.tcc01.ccfb[3].seq == 8 && msg->u.rtpfb.u.tcc01.ccfb[3].received == 1 &&
                   msg->u.rtpfb.u.tcc01.ccfb[3].ato == 64);
            assert(msg->u.rtpfb.u.tcc01.ccfb[4].seq == 9 && msg->u.rtpfb.u.tcc01.ccfb[4].received == 0);
            assert(msg->u.rtpfb.u.tcc01.ccfb[5].seq == 10 && msg->u.rtpfb.u.tcc01.ccfb[5].received == 0);
            assert(msg->u.rtpfb.u.tcc01.ccfb[6].seq == 11 && msg->u.rtpfb.u.tcc01.ccfb[6].received == 1 &&
                   msg->u.rtpfb.u.tcc01.ccfb[6].ato == 256);
            r = rtcp_rtpfb_tcc01_pack((uint16_t)msg->u.rtpfb.u.tcc01.begin, msg->u.rtpfb.u.tcc01.ccfb,
                                      msg->u.rtpfb.u.tcc01.count, msg->u.rtpfb.u.tcc01.timestamp,
                                      (uint8_t)msg->u.rtpfb.u.tcc01.cc, buffer, sizeof(buffer));
            assert(20 == r && 0 == memcmp(buffer, param, r));
            break;

        case 31: // test only
            assert(248 == msg->u.rtpfb.u.tcc01.begin && -5546573 == msg->u.rtpfb.u.tcc01.timestamp &&
                   128 == msg->u.rtpfb.u.tcc01.count && 1 == msg->u.rtpfb.u.tcc01.cc);
            assert(msg->u.rtpfb.u.tcc01.ccfb[0].seq == 248 && msg->u.rtpfb.u.tcc01.ccfb[0].received == 1 &&
                   msg->u.rtpfb.u.tcc01.ccfb[0].ato == 33);
            assert(msg->u.rtpfb.u.tcc01.ccfb[1].seq == 249 && msg->u.rtpfb.u.tcc01.ccfb[1].received == 0);
            assert(msg->u.rtpfb.u.tcc01.ccfb[125].seq == 373 && msg->u.rtpfb.u.tcc01.ccfb[125].received == 1 &&
                   msg->u.rtpfb.u.tcc01.ccfb[125].ato == -50);
            assert(msg->u.rtpfb.u.tcc01.ccfb[126].seq == 374 && msg->u.rtpfb.u.tcc01.ccfb[126].received == 0);
            assert(msg->u.rtpfb.u.tcc01.ccfb[127].seq == 375 && msg->u.rtpfb.u.tcc01.ccfb[127].received == 1 &&
                   msg->u.rtpfb.u.tcc01.ccfb[127].ato == 65);
            r = rtcp_rtpfb_tcc01_pack((uint16_t)msg->u.rtpfb.u.tcc01.begin, msg->u.rtpfb.u.tcc01.ccfb,
                                      msg->u.rtpfb.u.tcc01.count, msg->u.rtpfb.u.tcc01.timestamp,
                                      (uint8_t)msg->u.rtpfb.u.tcc01.cc, buffer, sizeof(buffer));
            assert(20 == r && 0 == memcmp(buffer, param, r));
            break;

        case RTCP_RTPFB_CCFB:
            assert(0);
            break;

        default:
            break;
        }
        break;

    default:
        assert(0);
    }
}

static void rtcp_rtpfb_nack_test(void)
{
    // rtcp_nack_t* nack;
    // const uint8_t data[] = { 0x81, 0xcd, 0x00, 0x08, 0x84, 0x68, 0xc2, 0x4c, 0x84, 0x68, 0xc2, 0x4c, 0x03, 0x27,
    // 0x7f, 0xff, 0x03, 0x38, 0xff, 0xf7, 0x03, 0x49, 0xff, 0xff, 0x03, 0x5a, 0xff, 0xff, 0x03, 0x6b, 0x7f, 0xbf, 0x03,
    // 0x7c, 0x00, 0x0f }; assert(0 == rtcp_rtpfb_nack_unpack(NULL, NULL, 0, 0, data, sizeof(data))); assert(nack[0].pid
    // == 807 && nack[0].blp == 0x7fff); assert(nack[1].pid == 824 && nack[1].blp == 0xfff7); assert(nack[2].pid == 841
    // && nack[2].blp == 0xffff); assert(nack[3].pid == 858 && nack[3].blp == 0xffff); assert(nack[4].pid == 875 &&
    // nack[4].blp == 0x7fbf); assert(nack[5].pid == 892 && nack[5].blp == 0x000f);

    const uint8_t data[] = {0x02, 0x77, 0x80, 0x28, 0x02, 0x88, 0x00, 0x21, 0x02, 0x9a, 0x00, 0x08, 0x02,
                            0xb2, 0x20, 0x00, 0x02, 0xde, 0x00, 0x25, 0x02, 0xf5, 0x11, 0x00, 0x03, 0x09,
                            0x00, 0x00, 0x03, 0x3a, 0x00, 0x02, 0x03, 0x61, 0x00, 0x00, 0x03, 0x72, 0x03,
                            0x00, 0x03, 0x8b, 0x04, 0x00, 0x03, 0xa3, 0x00, 0x00, 0x03, 0xc3, 0x00, 0x6d};

    struct rtcp_msg_t msg;
    struct rtp_context rtp;
    rtp.handler.on_rtcp = rtcp_on_rtpfb_test;
    rtp.cbparam = (void *)data;

    msg.type = (RTCP_RTPFB_NACK << 8) | RTCP_RTPFB;
    assert(0 == rtcp_rtpfb_nack_unpack(&rtp, NULL, &msg, data, sizeof(data)));
}

static void rtcp_rtpfb_tmmbr_test(void)
{
    const uint8_t data[] = {0x23, 0x45, 0x67, 0x89, 0x0a, 0x61, 0x61, 0xfe};

    struct rtcp_msg_t msg;
    struct rtp_context rtp;
    rtp.handler.on_rtcp = rtcp_on_rtpfb_test;
    rtp.cbparam = (void *)data;

    msg.type = (RTCP_RTPFB_TMMBR << 8) | RTCP_RTPFB;
    assert(0 == rtcp_rtpfb_tmmbr_unpack(&rtp, NULL, &msg, data, sizeof(data)));
}

static void rtcp_rtpfb_tmmbn_test(void)
{
    const uint8_t data[] = {0x23, 0x45, 0x67, 0x89, 0x0a, 0x61, 0x61, 0xfe};

    struct rtcp_msg_t msg;
    struct rtp_context rtp;
    rtp.handler.on_rtcp = rtcp_on_rtpfb_test;
    rtp.cbparam = (void *)data;

    msg.type = (RTCP_RTPFB_TMMBN << 8) | RTCP_RTPFB;
    assert(0 == rtcp_rtpfb_tmmbn_unpack(&rtp, NULL, &msg, data, sizeof(data)));
}

static void rtcp_rtpfb_tcc01_test(void)
{
    // rtcp_ccfb_t* ccfb;
    // const uint8_t data[] = { 0x8f, 0xcd, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x84, 0x68, 0xc2, 0x49, 0x23, 0x8b,
    // 0x01, 0x8b, 0x3e, 0x08, 0x49, 0x09, 0xd9, 0x00, 0x00, 0x0e, 0xa0, 0x00, 0x00, 0x1c, 0xa0, 0x00, 0x00, 0xe6, 0xa0,
    // 0x00, 0x00, 0x49, 0x20, 0x01, 0x2c, 0xff, 0xf0, 0x0c, 0x44, 0x94, 0x8c, 0x50, 0x00, 0x00 }; assert(0 ==
    // rtcp_rtpfb_tcc01_unpack(NULL, NULL, 0, 0, data, sizeof(data))); assert(395 == num && ccfb[0].seq == 9099 &&
    // ccfb[0].received && ccfb[1].ecn == 0x01 && ccfb[0].ato == 11); assert(cfb[1].seq == 9100 && ccfb[1].received &&
    // ccfb[1].ecn == 0x02 && ccfb[1].ato == -4);

    const uint8_t data[] = {0x06, 0xe7, 0x00, 0x65, 0x3e, 0xc6, 0x50, 0x2a, 0x9a, 0xff, 0x20, 0x16, 0x97, 0x68, 0xbc,
                            0xab, 0xa7, 0xfe, 0x20, 0x12, 0xc1, 0x50, 0x54, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00,
                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                            0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00,
                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                            0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01};
    struct rtcp_msg_t msg;
    struct rtp_context rtp;
    rtp.handler.on_rtcp = rtcp_on_rtpfb_test;
    rtp.cbparam = (void *)data;
    msg.type = (RTCP_RTPFB_TCC01 << 8) | RTCP_RTPFB;
    assert(0 == rtcp_rtpfb_tcc01_unpack(&rtp, NULL, &msg, data, sizeof(data)));

    // 11-768ms, 8-512ms, 7-448ms, 5-320ms
    const uint8_t data2[] = {0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x05, 0x00, 0xd2, 0x82,
                             0x00, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03};
    rtp.cbparam = (void *)data2;
    msg.type = (30 << 8) | RTCP_RTPFB;
    assert(0 == rtcp_rtpfb_tcc01_unpack(&rtp, NULL, &msg, data2, sizeof(data2)));

    // 248-33ms, 373-(-50)ms, 375-65ms, timestamp: -5546573
    const uint8_t data3[] = {0x00, 0xf8, 0x00, 0x80, 0xab, 0x5d, 0xb3, 0x01, 0xa0, 0x00,
                             0x00, 0x6f, 0xe2, 0x00, 0x84, 0xff, 0x38, 0x01, 0x04, 0x01};
    rtp.cbparam = (void *)data3;
    msg.type = (31 << 8) | RTCP_RTPFB;
    assert(0 == rtcp_rtpfb_tcc01_unpack(&rtp, NULL, &msg, data3, sizeof(data3)));
}

void rtcp_rtpfb_test(void)
{
    rtcp_rtpfb_nack_test();
    rtcp_rtpfb_tmmbr_test();
    rtcp_rtpfb_tmmbn_test();
    rtcp_rtpfb_tcc01_test();
}
#endif
